using System;
using System.IO;
using System.Net;
using Microsoft.Win32;
using System.Web.Services;
using System.Reflection;
using System.Xml;
using System.Xml.XPath;
using System.Web.Services.Protocols;

using System.Runtime.InteropServices;
using System.Globalization;
using System.Security.Principal;
using Microsoft.Crm;
using Microsoft.Crm.Sdk.Proxy;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CRM_Perf_BenchMark
{
    /// <summary>
    /// Summary description for ManagedABPSync
    /// </summary>
    [TestClass]
    public class ManagedABPSync
    {
       
        CRMEntity user = null;
        private string crmServer;
        private string m_ApiServer;

        XmlDocument resultsDoc = null;
        MySyncDataRecorder sdr = null;

        // Account fields
		const string c_wzAccountColumnSetInsert = "<columnset>"+
			"<column>accountid</column>"+
			"<column>name</column>"+
			"<column>telephone1</column>"+
			"<column>telephone2</column>"+
			"<column>fax</column>"+
			"<column>emailaddress1</column>"+
			"<column>donotemail</column>"+
			"<column>donotfax</column>"+
			"<column>donotphone</column>"+
			"<column>donotpostalmail</column>"+
			"<column>address1_line1</column>"+
			"<column>address1_line2</column>"+
			"<column>address1_line3</column>"+
			"<column>address1_city</column>"+
			"<column>address1_stateorprovince</column>"+
			"<column>address1_postalcode</column>"+
			"<column>address1_country</column>"+
			"<column>modifiedon</column>"+
			"</columnset>";

		const string c_wzAccountColumnSetDelete = "<columnset><column>accountid</column></columnset>";

		// Contact fields
		const string c_wzContactColumnSetInsert = "<columnset>"+
			"<column>contactid</column>"+
			"<column>fullname</column>"+
			"<column>jobtitle</column>"+
			"<column>accountid</column>"+
			"<column>telephone1</column>"+
			"<column>telephone2</column>"+
			"<column>fax</column>"+
			"<column>emailaddress1</column>"+
			"<column>donotemail</column>"+
			"<column>donotfax</column>"+
			"<column>donotphone</column>"+
			"<column>donotpostalmail</column>"+
			"<column>address1_line1</column>"+
			"<column>address1_line2</column>"+
			"<column>address1_line3</column>"+
			"<column>address1_city</column>"+
			"<column>address1_stateorprovince</column>"+
			"<column>address1_postalcode</column>"+
			"<column>address1_country</column>"+
			"<column>modifiedon</column>"+
			"</columnset>";

		const string c_wzContactColumnSetDelete = "<columnset><column>contactid</column></columnset>";

		// Lead fields
		const string c_wzLeadColumnSetInsert = "<columnset>"+
			"<column>leadid</column>"+
			"<column>fullname</column>"+
			"<column>subject</column>"+
			"<column>companyname</column>"+
			"<column>telephone1</column>"+
			"<column>telephone2</column>"+
			"<column>fax</column>"+
			"<column>emailaddress1</column>"+
			"<column>donotemail</column>"+
			"<column>donotfax</column>"+
			"<column>donotphone</column>"+
			"<column>donotpostalmail</column>"+
			"<column>address1_line1</column>"+
			"<column>address1_line2</column>"+
			"<column>address1_line3</column>"+
			"<column>address1_city</column>"+
			"<column>address1_stateorprovince</column>"+
			"<column>address1_postalcode</column>"+
			"<column>address1_country</column>"+
			"<column>modifiedon</column>"+
			"</columnset>";

		const string c_wzLeadColumnSetDelete = "<columnset><column>leadid</column></columnset>";

		// Queue fields
		const string c_wzQueueColumnSetInsert = "<columnset>"+
			"<column>queueid</column>"+
			"<column>name</column>"+
			"<column>emailaddress</column>"+
			"<column>modifiedon</column>"+
			"</columnset>";

		const string c_wzQueueColumnSetDelete = "<columnset><column>queueid</column></columnset>";

		// SystemUser fields
		const string c_wzUserColumnSetInsert = "<columnset>"+
			"<column>systemuserid</column>"+
			"<column>fullname</column>"+
			"<column>title</column>"+
			"<column>internalemailaddress</column>"+
			"<column>address1_telephone1</column>"+
			"<column>address1_telephone2</column>"+
			"<column>homephone</column>"+
			"<column>mobilephone</column>"+
			"<column>modifiedon</column>"+
			"</columnset>";

		const string c_wzUserColumnSetDelete = "<columnset><column>systemuserid</column></columnset>";

		int DELETED_ITEMS_BATCH_SIZE	= 100;
		int INSERTED_ITEMS_BATCH_SIZE	= 100;	// TODO:  DISCOVER OPTIMUM VALUES FOR THESE

		int enTotal = 5;
		
		static  string  c_wzPlatformNameAccount	= "Account";
		static  string  c_wzPlatformNameContact	= "Contact";
		static  string  c_wzPlatformNameLead = "Lead";
		static  string  c_wzPlatformNameQueue = "Queue";
		static  string  c_wzPlatformNameUser = "SystemUser";

		// NOTE:  THE ORDER OF THIS ARRAY MUST MATCH THE ORDER OF THE ENTITY_NAMES ENUM!
		static  string [] s_EntityNamesForSync = {
													 c_wzPlatformNameAccount,
													 c_wzPlatformNameContact,
													 c_wzPlatformNameLead,
													 c_wzPlatformNameQueue,
													 c_wzPlatformNameUser};
        string userName = string.Empty;
        string domain = string.Empty;
        string m_crmticket = string.Empty;
        string clientKeyPath = string.Empty;
        NetworkCredential nc = null;
        private ICredentials networkCredential = null;
        
        private String subscriptionId = null;
        CrmService _service = null;
        //string serverUrl;
        private RegistryKey regKey = null;
        string outDir = string.Empty;



        // Need this public property to don't dependent on RegControl in unit test, perf driver, etc.
        public String ClientKeyPath
        {
            get
            {
                if (clientKeyPath == null)
                    clientKeyPath = "SOFTWARE\\Microsoft\\MSCRMClient";

                return clientKeyPath;
            }

            set { clientKeyPath = value; }
        }

        // Need this public property for CreateSubscription so that we do not uase the Environemnt variable for user and domain
        public String UserName
        {
            get
            {
                if (userName == null)
                    userName = Environment.UserName;

                return userName;
            }

            set { userName = value; }
        }

        // Need this public property for CreateSubscription so that we do not uase the Environemnt variable for user and domain
        public String UserDomain
        {
            get
            {
                if (domain == null)
                    domain = Environment.UserDomainName;

                return domain;
            }

            set { domain = value; }
        }

        // Need this public property for webservice so that we do not use the default credential
        public System.Net.ICredentials NetworkCredential
        {
            get
            {
                if (networkCredential == null)


                    return System.Net.CredentialCache.DefaultCredentials;
                else
                    return networkCredential;

            }

            set { networkCredential = value; }
        }

        private RegistryKey RegKey
        {
            get
            {
                if (this.regKey == null)
                {
                    this.regKey = Registry.CurrentUser.OpenSubKey(ClientKeyPath, true);
                    if (this.regKey == null)
                        throw new Exception("Client registry subkey not found in the registry");
                }
                return this.regKey;
            }
        }


        private String SubscriptionId
        {
            get
            {
                if (this.subscriptionId == null)
                {
                    subscriptionId = (String)RegKey.GetValue("ABPSubscriptionId");
                }
                return this.subscriptionId;
            }
            set
            {
                subscriptionId = value;
                RegKey.SetValue("ABPSubscriptionId", subscriptionId);
            }
        }

        protected CrmService SyncService
        {
            get
            {
                if (_service == null)
                {
                    _service = new CrmService();
                    _service.Url = m_ApiServer + "/MSCRMServices/2007/CrmService.asmx";

                    
                    _service.UnsafeAuthenticatedConnectionSharing = true;
                    _service.Credentials = NetworkCredential;
                    _service.ConnectionGroupName = user["domainname"];
                    _service.Timeout = 1000000;
                    
                    CrmAuthenticationToken token = new CrmAuthenticationToken();
                    token.CallerId = new Guid(user[EntityIDNames.User]);
                    token.OrganizationName = user["organizationname"];
                    
                    token.AuthenticationType = AuthenticationType.AD;
                    
                    _service.CrmAuthenticationTokenValue = token;
                }
                return _service;
            }
        }

      
        private TestContext testContext;

        public ManagedABPSync()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        [ClassInitialize()]
        public static void MyClassInitialize(TestContext testContext)
        {
            //this is the directory where all perf results are stored
            string outlookUnitTestDir = Directory.GetDirectoryRoot(Directory.GetCurrentDirectory()) + @"outlooksyncdata\ManagedABPSync";
            if (!Directory.Exists(outlookUnitTestDir))
                Directory.CreateDirectory(outlookUnitTestDir);
        }

        /// <summary>
        /// Initialize() is called once during test execution before
        /// test methods in this test class are executed.
        /// </summary>
        [TestInitialize()]
        public void Initialize()
        {
            System.Diagnostics.Trace.WriteLine("init" + DateTime.Now.ToString());

			bool foundUser = false;

			while (!foundUser)
			{
				try
				{
					user = EntityManager.Instance.GetNextOutlookUser();
					foundUser = true;

				}
				catch (Exception ex)
				{
					//we could not get a user
					System.Diagnostics.Trace.WriteLine(ex.ToString());
					//lets sleep 5 seconds befor we try again
					System.Threading.Thread.Sleep(5000);

				}
			}

            try
            {
                crmServer = user["CrmServer"];
                m_ApiServer = user["ApiServer"];

                string[] splits = user["domainname"].Split(new char[] { '\\' }, 2, StringSplitOptions.RemoveEmptyEntries);
                if (splits.Length > 1)
                {
                    domain = splits[0];
                    userName = splits[1];
                }
                else
                {
                    string[] passportusersplit = user["passportname"].Split(new char[] { '@' }, 2, StringSplitOptions.RemoveEmptyEntries);
                    userName = passportusersplit[0];
                }

                //we know the user Name and passowrd, we need to get 
                //we are adding org id because we going to run tests for multiple orgs
                //user names can be same for different orgs
                clientKeyPath = "SOFTWARE\\Microsoft\\MSCRM" + userName + "_" + user["organizationid"];
                nc = new System.Net.NetworkCredential(userName, user["userpassword"], domain);
                NetworkCredential = nc;

                //we are going to see here if registry exists, if it does not then we are going to create the registry
                CreateRegistryForUser(nc);

                sdr = new MySyncDataRecorder();
                // create transfer data provider
                resultsDoc = new XmlDocument();
                resultsDoc.AppendChild(resultsDoc.CreateElement("run_results"));
            }

            catch (System.Web.Services.Protocols.SoapException ex)
            {
                System.Diagnostics.Trace.WriteLine("ManagedABPSync:User " + userName + " Soap Exception Detail:" + ex.Detail.InnerXml);
                throw ex;

            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine("ManagedABPSync:User " + userName + ":Exception Message:" + ex.Message);
                throw ex;
            }
        }

        /// <summary>
        /// Cleanup() is called once during test execution after
        /// test methods in this class have executed unless the
        /// corresponding Initialize() call threw an exception.
        /// </summary>
        [TestCleanup()]
        public void Cleanup()
        {
			if (user == null)
				return;
            System.Diagnostics.Trace.WriteLine("ManagedABPSync:cleanup" + DateTime.Now.ToString());

			EntityManager.Instance.FreeOutlookUser(user);
            
                try
                {
                    XmlDocument docNode = resultsDoc.DocumentElement.OwnerDocument;
                    XmlElement el;
                    el = sdr.SerializeToXML(resultsDoc.DocumentElement.OwnerDocument);
                    resultsDoc.DocumentElement.AppendChild(el);


                    string fileName = userName + "_" + "ManagedABP" + "_" + DateTime.Now.ToString() + ".xml";
                    fileName = fileName.Replace(" ", "_");
                    fileName = fileName.Replace("/", "_");
                    fileName = fileName.Replace(":", "_");
                    System.Diagnostics.Trace.WriteLine(fileName);
                    string dir = Environment.GetEnvironmentVariable("SystemDrive") + "\\Documents and Settings\\" + userName + "\\Application Data";
                    resultsDoc.Save(ConfigSettings.Default.OutlookSyncDir + "\\ManagedABPSync\\" + fileName);
                }

                catch { }
        }

        [TestMethod]
        public void ManagedABPSyncTM()
        {
			if (user == null)
				return;
          
            try
            {
                DateTime dtStart = DateTime.Now;
                DateTime dtFinish = DateTime.Now;

                if (SubscriptionId == null)
                {
                    testContext.BeginTimer("ManagedABPSync:CreateSubscription");
                    dtStart = DateTime.Now;
                    CreateOnlineSubscription();
                    dtFinish = DateTime.Now;
                    testContext.EndTimer("ManagedABPSync:CreateSubscription");

                    if (sdr != null)
                    {
                        sdr.CreateSubscription = dtFinish - dtStart;
                    }
                }

                // Step2 : PrepareSync
                testContext.BeginTimer("ManagedABPSync:DoPrepareSync");
                dtStart = DateTime.Now;
                String syncInfo = PrepareOnlineSync();
                dtFinish = DateTime.Now;
                testContext.EndTimer("ManagedABPSync:DoPrepareSync");
                if (sdr != null)
                {
                    sdr.PrepareSync = dtFinish - dtStart;
                    sdr.SyncInfo = syncInfo;
                }

                // Step2 : GetData

                SyncInfo[] syncData = new SyncInfo[enTotal];

                int total = 0;
                testContext.BeginTimer("ManagedABPSync:GetData");
                dtStart = DateTime.Now;
                int numEntities = GetData(syncInfo, ref syncData, ref total);
                dtFinish = DateTime.Now;
                testContext.EndTimer("ManagedABPSync:GetData");
                
                if (total == 0)
                    return;

                for (int j = 0; j < numEntities; j++)
                {
                    switch (syncData[j].entityName.ToLower())
                    {
                        case "account":
                            testContext.BeginTimer("ManagedABPSync:SyncItemByTypeForAccount");
                            System.Diagnostics.Trace.WriteLine("ManagedABPSync:Syncing accounts");
                            SyncItemByType(syncData[j], SubscriptionId, c_wzAccountColumnSetInsert, c_wzAccountColumnSetDelete);
                            testContext.EndTimer("ManagedABPSync:SyncItemByTypeForAccount");
                            break;

                        case "contact":
                            testContext.BeginTimer("ManagedABPSync:SyncItemByTypeForContact");
                            System.Diagnostics.Trace.WriteLine("ManagedABPSync:Syncing contacts");
                            SyncItemByType(syncData[j], SubscriptionId, c_wzContactColumnSetInsert, c_wzContactColumnSetDelete);
                            testContext.EndTimer("ManagedABPSync:SyncItemByTypeForContact");
                            break;
                        case "lead":
                            testContext.BeginTimer("ManagedABPSync:SyncItemByTypeForLead");
                            System.Diagnostics.Trace.WriteLine("ManagedABPSync:Syncing Leads");
                            SyncItemByType(syncData[j], SubscriptionId, c_wzLeadColumnSetInsert, c_wzLeadColumnSetDelete);
                            testContext.EndTimer("ManagedABPSync:SyncItemByTypeForLead");

                            break;
                        case "queue":
                            testContext.BeginTimer("ManagedABPSync:SyncItemByTypeForQueue");
                            System.Diagnostics.Trace.WriteLine("ManagedABPSync:Syncing Queue");
                            SyncItemByType(syncData[j], SubscriptionId, c_wzQueueColumnSetInsert, c_wzQueueColumnSetDelete);
                            testContext.EndTimer("ManagedABPSync:SyncItemByTypeForQueue");
                            break;
                        case "systemuser":
                            testContext.BeginTimer("ManagedABPSync:SyncItemByTypeForSystemUser");
                            System.Diagnostics.Trace.WriteLine("ManagedABPSync:Syncing Users");
                            SyncItemByType(syncData[j], SubscriptionId, c_wzUserColumnSetInsert, c_wzUserColumnSetDelete);
                            testContext.EndTimer("ManagedABPSync:SyncItemByTypeForSystemUser");
                            break;
                        default:
                            break;
                    }
                }
                dtFinish = DateTime.Now;
                if (sdr != null)
                    sdr.MoveData = dtFinish - dtStart;

                System.Diagnostics.Trace.WriteLine("ManagedABPSync:User:" + userName + " SyncEnd:" + DateTime.Now.ToString());
            }

            catch (SoapException e)
            {
                System.Diagnostics.Trace.WriteLine("ManagedABPSync:User:" + userName + " Begin Got SOAP exception");
                System.Diagnostics.Trace.WriteLine(e.Detail.InnerXml);
                System.Diagnostics.Trace.WriteLine("ManagedABPSync:User:" + userName + " End Got SOAP exception");
                throw e;
            }

            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine("ManagedABPSync:User:" + userName + " Begin Got exception");
                System.Diagnostics.Trace.WriteLine(e.ToString());
                System.Diagnostics.Trace.WriteLine("ManagedABPSync:User:" + userName + " End Got exception");
                throw e;
            }
        }

        private XmlDocument CreateXmlDocument(string xml)
        {
            XmlTextReader reader = new XmlTextReader(new StringReader(xml));

            reader.ProhibitDtd = true;
            reader.WhitespaceHandling = WhitespaceHandling.Significant;

            XmlDocument xmlDoc = new XmlDocument();

            // Set XmlResolver to null to prevent usage of the default XmlResolver.
            xmlDoc.XmlResolver = null;

            xmlDoc.Load(reader);
            reader.Close();

            return xmlDoc;
        }

        private int ConvertAttributeToInt(XmlAttribute attribute)
        {
            if (attribute == null)
                return 0;

            try
            {
                int count = Int32.Parse(attribute.Value);
                return count;
            }
            catch
            {
                throw new CrmArgumentException("Cannot convert attribute to integer.", attribute.Value);
            }
        }

        public void CreateOnlineSubscription()
        {
            try
            {
                subscription subsc = new subscription();
                subsc.subscriptiontype = new CrmNumber();
                subsc.subscriptiontype.Value = SubscriptionType.AddressBookProvider;
                subsc.machinename = Environment.UserDomainName + "\\" + Environment.MachineName; ;

                CreateSubscriptionRequest request = new CreateSubscriptionRequest();
                request.Target = subsc;

                CreateSubscriptionResponse response = (CreateSubscriptionResponse)SyncService.Execute(request);

                SubscriptionId = response.id.ToString("B").ToUpper();
            }
            catch (SoapException e)
            {
                throw e;
            }
        }

        public String PrepareOnlineSync()
        {
            try
            {
                PrepareSyncSubscriptionRequest request = new PrepareSyncSubscriptionRequest();
                request.SubscriptionId = new Guid(SubscriptionId);
                PrepareSyncSubscriptionResponse response = (PrepareSyncSubscriptionResponse)SyncService.Execute(request);
                return response.SyncInfoXml;
            }
            catch (SoapException e)
            {
                throw e;
            }
        }

        public String GetOnlineData(String subscriptionId, String entityName, Microsoft.Crm.Sdk.Proxy.SyncAction syncAction, int batchSize, String columnSetXml)
        {
            try
            {
                GetSyncDataSubscriptionRequest request = new GetSyncDataSubscriptionRequest();
                request.BatchSize = batchSize;
                request.ColumnSetXml = columnSetXml;
                request.EntityName = entityName;
                request.SubscriptionId = new Guid(subscriptionId);
                request.SyncAction = syncAction;
                GetSyncDataSubscriptionResponse response = (GetSyncDataSubscriptionResponse)SyncService.Execute(request);
                return response.SyncDataXml;
            }
            catch (SoapException e)
            {
                throw e;
            }
        }

        public void PostOnlineSync(String subscriptionId, String entityName, Microsoft.Crm.Sdk.Proxy.SyncAction syncAction, int batchSize)
        {
            try
            {
                PostSyncSubscriptionRequest request = new PostSyncSubscriptionRequest();
                request.BatchSize = batchSize;
                request.EntityName = entityName;
                request.SubscriptionId = new Guid(subscriptionId);
                request.SyncAction = syncAction;
                SyncService.Execute(request);
            }
            catch (SoapException e)
            {
                throw e;
            }
        }

        private int GetData(String syncInfo, ref SyncInfo [] syncData, ref int total) 
		{
			System.Diagnostics.Trace.WriteLine(syncInfo);
	
			XmlDocument doc = CreateXmlDocument(syncInfo);
			XPathNavigator nav = doc.CreateNavigator();
			XPathNodeIterator iterator = nav.Select("/result/entity");

			int insertCount = 0;
			int deleteCount = 0;
			int index = 0;
			while (iterator.MoveNext())
			{
				XmlNode node = ((IHasXmlNode)iterator.Current).GetNode();
				XmlAttributeCollection xmlAttributes = node.Attributes;

				String entityName = xmlAttributes["name"].Value;
				
				deleteCount = ConvertAttributeToInt(xmlAttributes["delete_count"]);
				insertCount = ConvertAttributeToInt(xmlAttributes["insert_count"]);
				syncData[index] = new SyncInfo();
				syncData[index].entityName = entityName;
				syncData[index].deleteCount = deleteCount;
				syncData[index].insertCount = insertCount;

				System.Diagnostics.Trace.WriteLine("Entity:" + syncData[index].entityName);
				System.Diagnostics.Trace.WriteLine("deleteCount:" + syncData[index].deleteCount);
				System.Diagnostics.Trace.WriteLine("insertCount:" + syncData[index].insertCount);

				index++;
				total += insertCount + deleteCount;
			}
			return index;
		}

		private void SyncItemByType(SyncInfo syncData, string id, string insertSet, string deleteSet) 
		{
			int numberofItems = syncData.insertCount + syncData.deleteCount;

			System.Diagnostics.Trace.WriteLine("User:" + userName + " numberofItems:" + numberofItems + " " + DateTime.Now.ToString());
			if(numberofItems == 0)
				return;

			bool moreRecords = true;
			if(syncData.deleteCount > 0) 
			{
				while(moreRecords) 
				{
                    string deletedItems = GetOnlineData(SubscriptionId, syncData.entityName, SyncAction.Delete, DELETED_ITEMS_BATCH_SIZE, deleteSet);
					XmlDocument doc = CreateXmlDocument(deletedItems);
					XmlNodeList delNodeList = doc.FirstChild.ChildNodes;

					if(delNodeList.Count == 0) 
					{
						moreRecords = false;
						continue;
					}

					for(int i = 0; i < delNodeList.Count; i++) 
					{
						XmlNode node = delNodeList[i];
						//System.Diagnostics.Trace.WriteLine("Sleeping for 10msec for a delete");
						//delete item from outlook
						//System.Threading.Thread.Sleep(10);
					}
                    PostOnlineSync(SubscriptionId, syncData.entityName, SyncAction.Delete, DELETED_ITEMS_BATCH_SIZE);
				}
			}

			if(syncData.insertCount > 0) 
			{
				moreRecords = true;
				while(moreRecords) 
				{
                    string insertedItems = GetOnlineData(SubscriptionId, syncData.entityName, SyncAction.Insert, INSERTED_ITEMS_BATCH_SIZE, insertSet);
					XmlDocument doc = CreateXmlDocument(insertedItems);
					
					XmlNodeList insertNodeList = doc.FirstChild.ChildNodes;

					if(insertNodeList.Count == 0) 
					{
						moreRecords = false;
						continue;
					}

					for(int i = 0; i < insertNodeList.Count; i++) 
					{
						XmlNode node = insertNodeList[i];
						//System.Diagnostics.Trace.WriteLine("Sleeping for 100msec for an insert");
						//insert item into outlook
						//System.Threading.Thread.Sleep(100);
					}
                    PostOnlineSync(SubscriptionId, syncData.entityName, SyncAction.Insert, INSERTED_ITEMS_BATCH_SIZE);
				}
			}
		}

        private void CreateRegistryForUser(System.Net.NetworkCredential nc)
        {
            string userId = user["systemuserid"];
            string businessId = user["businessunitid"];

            RegistryKey userRegKey = Registry.CurrentUser.OpenSubKey(clientKeyPath, true);
            if (userRegKey == null)
                userRegKey = Registry.CurrentUser.CreateSubKey(clientKeyPath);

            //we need to create - BusinessId, UserId, ServerUrl
            userRegKey.SetValue("BusinessId", "{" + businessId + "}", RegistryValueKind.String);
            userRegKey.SetValue("UserId", "{" + userId + "}", RegistryValueKind.String);
            userRegKey.SetValue("ServerUrl", crmServer + "/MSCRMServices", RegistryValueKind.String);
        }

        public TestContext TestContext
        {
            set { testContext = value; }
            get { return testContext; }
        }
    }
}
